package com.significantfiles.properties.config.metadata;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import com.significantfiles.properties.util.config.guice.GetterInterface;



/**
 * Manage getter and setter of a passed type.
 * 
 * @author Andreas Siebert, contentteam AG (asiebert@contentteam.com)
 */
public class JavaBeansInterface implements GetterInterface {

	private final Log LOG = new Log();
	
	private List<Method> getter = new ArrayList<Method>(10);

	private List<Method> setter = new ArrayList<Method>(10);
	
	private final Class<? extends Object> thisClass;
	
	
	public JavaBeansInterface(final Class<? extends Object> source) {
		this.thisClass = source;
		
		this.checkThisClass();
		
		this.parseInterface();
	}
	
//	public List<Method> getterMethods() {
//		return Collections.unmodifiableList(this.getter);
//	}
	
	public Iterator<Method> getterMethods() {
		return this.getter.iterator();
	}
	
	private void manageGetter(final Method method, MethodType methodType) {
		this.getter.add(method);
	}

	private void manageSetter(final Method method, final MethodType methodType) {
		this.setter.add(method);
	}
	
	private MethodType identifyMethodType(final Method method) {
		final boolean isPublicMethod = Modifier.isPublic(method.getModifiers());

		if (!isPublicMethod)
			return MethodType.IGNORE;

		final String name = method.getName();

		if (isSetterMethod(method, name)) {
			return MethodType.SETTER;
		}

		if (isGetterMethod(method, name)) {
			return MethodType.GETTER;
		}

		return MethodType.IGNORE;
	}

	private static boolean isGetterMethod(final Method method, final String name) {
		final boolean startsWithGet = name.startsWith("get");
		final boolean hasResultType = !method.getReturnType()
				.equals(Void.class);

		return startsWithGet && hasResultType;
	}

	private static boolean isSetterMethod(final Method method, final String name) {
		final boolean startsWithSet = name.startsWith("set");
		final boolean isVoid = method.getReturnType().equals(Void.TYPE);
		final boolean isPropertyMethod = method.getAnnotation(Property.class) != null;

		final Class<?>[] parameterTypes = method.getParameterTypes();

		final boolean hasOneParameter = (parameterTypes.length == 1);

		return startsWithSet && isVoid && isPropertyMethod && hasOneParameter;
	}
	
	private void parseInterface() {
		final Method[] methods = thisClass.getMethods();

		for (int i = 0; i < methods.length; i++) {
			final Method method = methods[i];

			final MethodType methodType = identifyMethodType(method);

			if (MethodType.SETTER.equals(methodType)) {
				manageSetter(method, methodType);

				continue;
			}

			if (MethodType.GETTER.equals(methodType)) {
				manageGetter(method, methodType);

				continue;
			}
		}
	}

	public List<Method> setterMethods() {
		return Collections.unmodifiableList(this.setter);
	}

	private void checkThisClass() {
		final boolean isNotAcceptableClass = thisClass.isLocalClass()
				|| thisClass.isAnnotation() || thisClass.isAnonymousClass()
				|| thisClass.isInterface();

		if (isNotAcceptableClass) {
			final String logMsg = LOG.classIsntAcceptable(thisClass);

			throw new RuntimeException(logMsg);
		}
	}
	

	/**
	 * Log-Methods: all buisiness-logs are defined here.
	 */
	private static class Log {

		private final Logger logger = Logger.getLogger( JavaBeansInterface.class );
		
		public String classIsntAcceptable(final Class<? extends Object> thisClass) {
			final String logMsg = "Class [="
					+ thisClass.getName()
					+ "] isn't public. Nested, annonymous and non-public-classes doesn't supported by this factory.";

			logger.error(logMsg);

			return logMsg;
		}
	}
}
